#!/usr/bin/python2.3
# File: getGPS		-*- Python -*-
# Created by: Alex Knowles (alex@bloodyeck.com) Wed Nov 17 10:42:58 2004
# Last Modified: Time-stamp: <Wed Jul 12 12:34:04 PDT 2006>
# RCS $Id: getGPS,v 1.10 2006-10-22 22:18:40 ark Exp $

"""Loads GPS tracklog info from my gps

Usage: %(PROGRAM)s [options]

Options:
    -g gpxfile
    --gpx=gpxfile
        Use this GPX XML file, don't download from Serial Port

    -p positions.xml
    --positionxml=positions.xml
        Use this XML file

    -o outputdir
    --outputdir=some/dir/name
        Output Files to this directory

    -h/--help
        Print this message and exit.
"""

import os
import sys
import socket
import datetime
import getopt
import logging
import math
import time

from xml.dom import minidom

def usage(code, msg=''):
  if code:
    fd = sys.stderr
  else:
    fd = sys.stdout
  print >> fd, __doc__
  if msg:
    print >> fd, msg
  sys.exit(code)

def Main(argv):
  try:
    opts, args = getopt.getopt(argv[1:], 'g:p:o:h',
                               ['gpx=', 'positionxml=', 'outputdir=', 'help'])
  except getopt.error, msg:
    usage(1, msg)

  if args:
    usage(1)

  gpx = None
  tmpfile = None
  posxml = None
  outputdir = os.path.join(os.environ['HOME'], "html", "velonerds",
                           "maps", "gpx")

  for opt, arg in opts:
    if opt in ('-h', '--help'):
      usage(0)
    if opt in ('-g', '--gpx'):
      gpx = arg
    if opt in ('-p', '--positionxml'):
      posxml = arg
    if opt in ('-o', '--outputdir'):
      outputdir = arg

  if not os.path.isdir(outputdir):
    logging.fatal("Output Directory '%s' does not exist", outputdir)
    return

  if not posxml:
    posxml = os.path.join(outputdir, "places.xml")

  if not os.path.exists(posxml):
    logging.warning("Places XML does not exist at: '%d' unable to name logs",
                    posxml)

  if not gpx:
    logging.info("Loading Data from GPS device")
    gpx = LoadGpxFromSerial()
    tmpfile = gpx
    logging.info("Loaded GPS Data into temp file: %s", tmpfile)

  # now split up that file
  SplitGpx(gpx, posxml, outputdir)
  # TODO(ark) get photos and their positions

  if tmpfile:
    logging.info("Removing Temporary file: %s", tmpfile)
    os.remove(tmpfile)

def LoadGpxFromSerial():
  if socket.gethostname() in ["ark-mac.local", "mini.local"]:
    port = '/dev/cu.KeySerial1*'
  else:
    port = '/dev/ttyS0'

  f = os.tmpnam()
  options = '-x track,pack,split=4h,title="LOG # %c"'
  cmd = "gpsbabel -t -i garmin -f %s %s -o  gpx -F %s" % (port, options, f)
  print "%s\n" % cmd;
  if os.system(cmd):
    print "Failed\n"
    sys.exit(65)
  return f

def SplitGpx(gpxfile, posfile, outputdir):
    try:
      xmlfile = file(posfile)
      pos = minidom.parse(xmlfile)
      xmlfile.close()

      xmlfile = file(gpxfile)
      gpx = minidom.parse(xmlfile)
      xmlfile.close()
    except Exception, err:
      logging.error('XML Loading FAILED: %s, could not parse %s',
                    str(err), pos)
      raise

    tracks = gpx.getElementsByTagName("trk")
    logging.info("There are %d tracks", len(tracks))
    for track in tracks:
      name = GetNameForTrack(track, pos)
      if name:
        fname = os.path.join(outputdir, name + ".gpx")
        if os.path.exists(fname):
          logging.warning("Not writing Tracklog '%s' "
                          "since output file already exists", name)
        else:
          logging.info("Writing; %s" % fname)
          out = open(fname, "w")
          PrintGpXHeader(out)
          track.writexml(out)
          PrintGpXFooter(out)
          out.close()

def PrintGpXHeader(out):
  print >> out, """<?xml version="1.0"?>
<gpx
 version="1.0"
creator="getGPS - http://www.velonerds.com/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/0"
xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
<time>%s</time>""" % time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

def PrintGpXFooter(out):
  print >>out, "</gpx>"

def GetNameForTrack(track, pos):
  dist = -1
  name = ""
  datestr = ""
  arr = track.getElementsByTagName('trkpt')
  if arr:
    first = arr[0]
    for place in pos.getElementsByTagName("wpt"):
      nd = GetDistanceBetween(first, place)
      if dist == -1 or (nd >= 0 and nd < dist):
        dist = nd
        name = place.getElementsByTagName('cmt')[0].firstChild.data

    arr = first.getElementsByTagName('time')
    if arr:
      datestr = arr[0].firstChild.data[0:10]
  # logging.debug("Distance for %s is %.2f", name, dist)
  if dist > 1000:
    logging.info('Closest point (to lat="%s" lon="%s") was %s '
                 "which is %dm away which is too far to use",
                 first.getAttribute('lat'), first.getAttribute('lon'),
                 name, dist)
    name = ''
  if datestr:
    if name:
      name = "-".join((datestr, name))
    else:
      name = datestr
  return name.replace(' ', '_').replace('/', '_')

# from http://www.zend.com/codex.php?id=1486&single=1
# returns distance between two points in meters
def GetDistanceBetween(a, b):
  lat1 = math.radians(float(a.getAttribute('lat')))
  lon1 = math.radians(float(a.getAttribute('lon')))
  lat2 = math.radians(float(b.getAttribute('lat')))
  lon2 = math.radians(float(b.getAttribute('lon')))

  # Find the deltas
  delta_lat = lat2 - lat1
  delta_lon = lon2 - lon1

  # Find the Great Circle distance
  temp = (math.pow(math.sin(delta_lat / 2.0), 2) +
          math.cos(lat1) * math.cos(lat2) *
          math.pow(math.sin(delta_lon / 2.0), 2))
  distance = 6378000.0 * 2.0 * math.atan2(math.sqrt(temp),
                                          math.sqrt(1.0 - temp))
  return distance

if __name__ == '__main__':
  logging.basicConfig()
  logging.getLogger().setLevel(logging.DEBUG)
  logging.info("Starting my app")
  Main(sys.argv)
